/*-------------------------------------------------------------------------
 *
 * pg_buffercache_pages.c
 *	  显示缓冲区缓存的一些内容
 *
 *	  contrib/pg_buffercache/pg_buffercache_pages.c
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"


#define NUM_BUFFERCACHE_PAGES_MIN_ELEM	8
#define NUM_BUFFERCACHE_PAGES_ELEM	9

PG_MODULE_MAGIC;

/*
 * 记录结构，持有待曝光的缓存数据。
 */
typedef struct
{
	uint32		bufferid;
	Oid			relfilenode;
	Oid			reltablespace;
	Oid			reldatabase;
	ForkNumber	forknum;
	BlockNumber blocknum;
	bool		isvalid;
	bool		isdirty;
	uint16		usagecount;

	/*
	 * 一个 int32 足够大，因为 MAX_BACKENDS 防止一个缓冲区
	 * 被过多的后端固定，并且每个后端仅因 bufmgr.c 的
	 * PrivateRefCount 基础设施而固定一次。
	 */
	int32		pinning_backends;
} BufferCachePagesRec;


/*
 * 函数上下文，用于在重复调用中持久化数据。
 */
typedef struct
{
	TupleDesc	tupdesc;
	BufferCachePagesRec *record;
} BufferCachePagesContext;


/*
 * 从共享缓冲区缓存返回数据的函数 - 缓冲区号，
 * 关系节点/表空间/数据库/块号和脏标志。
 */
PG_FUNCTION_INFO_V1(pg_buffercache_pages);

Datum pg_buffercache_pages(PG_FUNCTION_ARGS)
{
	FuncCallContext *fc_funcctx;
	Datum		fc_result;
	MemoryContext fc_oldcontext;
	BufferCachePagesContext *fc_fctx;	/* 用户函数上下文。 */
	TupleDesc	fc_tupledesc;
	TupleDesc	fc_expected_tupledesc;
	HeapTuple	fc_tuple;

	if (SRF_IS_FIRSTCALL())
	{
		int			fc_i;

		fc_funcctx = SRF_FIRSTCALL_INIT();

		/* 在分配将用于后续调用的内容时切换上下文 */
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		/* 为跨调用持久性创建用户函数上下文 */
		fc_fctx = (BufferCachePagesContext *) palloc(sizeof(BufferCachePagesContext));

		/*
		 * 为了顺利支持从该扩展的版本 1.0 升级，
		 * 透明地处理（不存在）pinning_backends
		 * 列的问题。我们不幸的是必须获取结果类型... -
		 * 我们不能使用通过函数定义确定的结果类型，
		 * 否则在有人使用旧的（甚至错误的）函数定义时可能会崩溃。
		 */
		if (get_call_result_type(fcinfo, NULL, &fc_expected_tupledesc) != TYPEFUNC_COMPOSITE)
			elog(ERROR, "return type must be a row type");

		if (fc_expected_tupledesc->natts < NUM_BUFFERCACHE_PAGES_MIN_ELEM ||
			fc_expected_tupledesc->natts > NUM_BUFFERCACHE_PAGES_ELEM)
			elog(ERROR, "incorrect number of output arguments");

		/* 为结果行构造一个元组描述符。 */
		fc_tupledesc = CreateTemplateTupleDesc(fc_expected_tupledesc->natts);
		TupleDescInitEntry(fc_tupledesc, (AttrNumber) 1, "bufferid",
						   INT4OID, -1, 0);
		TupleDescInitEntry(fc_tupledesc, (AttrNumber) 2, "relfilenode",
						   OIDOID, -1, 0);
		TupleDescInitEntry(fc_tupledesc, (AttrNumber) 3, "reltablespace",
						   OIDOID, -1, 0);
		TupleDescInitEntry(fc_tupledesc, (AttrNumber) 4, "reldatabase",
						   OIDOID, -1, 0);
		TupleDescInitEntry(fc_tupledesc, (AttrNumber) 5, "relforknumber",
						   INT2OID, -1, 0);
		TupleDescInitEntry(fc_tupledesc, (AttrNumber) 6, "relblocknumber",
						   INT8OID, -1, 0);
		TupleDescInitEntry(fc_tupledesc, (AttrNumber) 7, "isdirty",
						   BOOLOID, -1, 0);
		TupleDescInitEntry(fc_tupledesc, (AttrNumber) 8, "usage_count",
						   INT2OID, -1, 0);

		if (fc_expected_tupledesc->natts == NUM_BUFFERCACHE_PAGES_ELEM)
			TupleDescInitEntry(fc_tupledesc, (AttrNumber) 9, "pinning_backends",
							   INT4OID, -1, 0);

		fc_fctx->tupdesc = BlessTupleDesc(fc_tupledesc);

		/* 分配 NBuffers 数量的 BufferCachePagesRec 记录。 */
		fc_fctx->record = (BufferCachePagesRec *)
			MemoryContextAllocHuge(CurrentMemoryContext,
								   sizeof(BufferCachePagesRec) * NBuffers);

		/* 设置最大调用并记住用户函数上下文。 */
		fc_funcctx->max_calls = NBuffers;
		fc_funcctx->user_fctx = fc_fctx;

		/* 在分配临时内存时返回到原始上下文 */
		MemoryContextSwitchTo(fc_oldcontext);

		/*
		 * 扫描所有缓冲区，保存相关字段到
		 * fctx->record 结构中。
		 *
		 * 我们不持有分区锁，因此无法获得所有缓冲区的一致
		 * 快照，但我们确实获取缓冲区头
		 * 锁，因此每个缓冲区的信息是自一致的。
		 */
		for (fc_i = 0; fc_i < NBuffers; fc_i++)
		{
			BufferDesc *fc_bufHdr;
			uint32		fc_buf_state;

			fc_bufHdr = GetBufferDescriptor(fc_i);
			/* 在检查之前锁定每个缓冲区头。 */
			fc_buf_state = LockBufHdr(fc_bufHdr);

			fc_fctx->record[fc_i].bufferid = BufferDescriptorGetBuffer(fc_bufHdr);
			fc_fctx->record[fc_i].relfilenode = fc_bufHdr->tag.rnode.relNode;
			fc_fctx->record[fc_i].reltablespace = fc_bufHdr->tag.rnode.spcNode;
			fc_fctx->record[fc_i].reldatabase = fc_bufHdr->tag.rnode.dbNode;
			fc_fctx->record[fc_i].forknum = fc_bufHdr->tag.forkNum;
			fc_fctx->record[fc_i].blocknum = fc_bufHdr->tag.blockNum;
			fc_fctx->record[fc_i].usagecount = BUF_STATE_GET_USAGECOUNT(fc_buf_state);
			fc_fctx->record[fc_i].pinning_backends = BUF_STATE_GET_REFCOUNT(fc_buf_state);

			if (fc_buf_state & BM_DIRTY)
				fc_fctx->record[fc_i].isdirty = true;
			else
				fc_fctx->record[fc_i].isdirty = false;

			/* 注意缓冲区是否有效以及是否已创建存储 */
			if ((fc_buf_state & BM_VALID) && (fc_buf_state & BM_TAG_VALID))
				fc_fctx->record[fc_i].isvalid = true;
			else
				fc_fctx->record[fc_i].isvalid = false;

			UnlockBufHdr(fc_bufHdr, fc_buf_state);
		}
	}

	fc_funcctx = SRF_PERCALL_SETUP();

	/* 获取保存的状态 */
	fc_fctx = fc_funcctx->user_fctx;

	if (fc_funcctx->call_cntr < fc_funcctx->max_calls)
	{
		uint32		fc_i = fc_funcctx->call_cntr;
		Datum		fc_values[NUM_BUFFERCACHE_PAGES_ELEM];
		bool		fc_nulls[NUM_BUFFERCACHE_PAGES_ELEM];

		fc_values[0] = Int32GetDatum(fc_fctx->record[fc_i].bufferid);
		fc_nulls[0] = false;

		/*
		 * 如果缓冲区未使用或无效，则将所有字段
		 * 设置为 null，除了 bufferid。
		 */
		if (fc_fctx->record[fc_i].blocknum == InvalidBlockNumber ||
			fc_fctx->record[fc_i].isvalid == false)
		{
			fc_nulls[1] = true;
			fc_nulls[2] = true;
			fc_nulls[3] = true;
			fc_nulls[4] = true;
			fc_nulls[5] = true;
			fc_nulls[6] = true;
			fc_nulls[7] = true;
			/* 对于 v1.0 调用者未使用，但数组总是足够长 */
			fc_nulls[8] = true;
		}
		else
		{
			fc_values[1] = ObjectIdGetDatum(fc_fctx->record[fc_i].relfilenode);
			fc_nulls[1] = false;
			fc_values[2] = ObjectIdGetDatum(fc_fctx->record[fc_i].reltablespace);
			fc_nulls[2] = false;
			fc_values[3] = ObjectIdGetDatum(fc_fctx->record[fc_i].reldatabase);
			fc_nulls[3] = false;
			fc_values[4] = ObjectIdGetDatum(fc_fctx->record[fc_i].forknum);
			fc_nulls[4] = false;
			fc_values[5] = Int64GetDatum((int64) fc_fctx->record[fc_i].blocknum);
			fc_nulls[5] = false;
			fc_values[6] = BoolGetDatum(fc_fctx->record[fc_i].isdirty);
			fc_nulls[6] = false;
			fc_values[7] = Int16GetDatum(fc_fctx->record[fc_i].usagecount);
			fc_nulls[7] = false;
			/* 对于 v1.0 调用者未使用，但数组总是足够长 */
			fc_values[8] = Int32GetDatum(fc_fctx->record[fc_i].pinning_backends);
			fc_nulls[8] = false;
		}

		/* 构建并返回元组。 */
		fc_tuple = heap_form_tuple(fc_fctx->tupdesc, fc_values, fc_nulls);
		fc_result = HeapTupleGetDatum(fc_tuple);

		SRF_RETURN_NEXT(fc_funcctx, fc_result);
	}
	else
		SRF_RETURN_DONE(fc_funcctx);
}
